<?php
// 数据库操作类
// 封装所有与数据库相关的操作

require_once 'config.php';

class Database {
    private $pdo;
    
    public function __construct() {
        error_log("正在创建数据库连接实例");
        $this->pdo = getDbConnection();
        error_log("数据库连接实例创建完成");
    }
    
    /**
     * 获取项目列表
     * @param int $pageno 页码
     * @param int $pagesize 每页数量
     * @return array 项目列表和总数
     */
    public function getProjectList($pageno = 1, $pagesize = 100) {
        try {
            error_log("开始获取项目列表，页码: " . $pageno . "，每页数量: " . $pagesize);
            
            // 计算偏移量
            $offset = ($pageno - 1) * $pagesize;
            
            // 获取总数
            error_log("执行COUNT查询");
            $countStmt = $this->pdo->prepare("SELECT COUNT(*) as count FROM projects");
            $countStmt->execute();
            $countResult = $countStmt->fetch();
            $count = (int)$countResult['count'];
            error_log("COUNT查询结果: " . $count);
            
            // 获取项目列表
            error_log("执行项目列表查询");
            $stmt = $this->pdo->prepare("SELECT id, flag, title, `desc`, content, update_time as updateTime FROM projects ORDER BY update_time DESC LIMIT :offset, :pagesize");
            $stmt->bindValue(':offset', (int)$offset, PDO::PARAM_INT);
            $stmt->bindValue(':pagesize', (int)$pagesize, PDO::PARAM_INT);
            $stmt->execute();
            $list = $stmt->fetchAll();
            error_log("项目列表查询完成，获取到 " . count($list) . " 条记录");
            
            // 对输出数据进行HTML解码，确保正确显示
            foreach ($list as &$item) {
                $item['title'] = htmlspecialchars_decode($item['title'], ENT_QUOTES);
                $item['desc'] = htmlspecialchars_decode($item['desc'], ENT_QUOTES);
            }
            
            return [
                'list' => $list,
                'count' => $count
            ];
        } catch (Exception $e) {
            error_log("获取项目列表失败: " . $e->getMessage());
            error_log("错误代码: " . $e->getCode());
            error_log("错误文件: " . $e->getFile());
            error_log("错误行号: " . $e->getLine());
            throw new Exception("获取项目列表失败: " . $e->getMessage());
        }
    }
    
    /**
     * 根据flag获取项目
     * @param string $flag 项目标识
     * @return array|null 项目信息
     */
    public function getProjectByFlag($flag) {
        try {
            error_log("开始根据flag获取项目: " . $flag);
            $stmt = $this->pdo->prepare("SELECT id, flag, title, `desc`, content, update_time as updateTime FROM projects WHERE flag = :flag");
            $stmt->bindParam(':flag', $flag);
            $stmt->execute();
            $project = $stmt->fetch();
            error_log("获取项目结果: " . ($project ? "找到项目" : "未找到项目"));
            
            // 对输出数据进行HTML解码，确保正确显示
            if ($project) {
                $project['title'] = htmlspecialchars_decode($project['title'], ENT_QUOTES);
                $project['desc'] = htmlspecialchars_decode($project['desc'], ENT_QUOTES);
            }
            
            return $project;
        } catch (Exception $e) {
            error_log("获取项目失败: " . $e->getMessage());
            throw new Exception("获取项目失败: " . $e->getMessage());
        }
    }
    
    /**
     * 根据ID获取项目
     * @param int $id 项目ID
     * @return array|null 项目信息
     */
    public function getProjectById($id) {
        try {
            error_log("开始根据ID获取项目: " . $id);
            $stmt = $this->pdo->prepare("SELECT id, flag, title, `desc`, content, update_time as updateTime FROM projects WHERE id = :id");
            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
            $stmt->execute();
            $project = $stmt->fetch();
            error_log("获取项目结果: " . ($project ? "找到项目" : "未找到项目"));
            
            // 对输出数据进行HTML解码，确保正确显示
            if ($project) {
                $project['title'] = htmlspecialchars_decode($project['title'], ENT_QUOTES);
                $project['desc'] = htmlspecialchars_decode($project['desc'], ENT_QUOTES);
            }
            
            return $project;
        } catch (Exception $e) {
            error_log("获取项目失败: " . $e->getMessage());
            throw new Exception("获取项目失败: " . $e->getMessage());
        }
    }
    
    /**
     * 保存项目
     * @param string $flag 项目标识
     * @param string $title 项目标题
     * @param string $desc 项目描述
     * @param string $content 项目内容
     * @return array|bool 保存成功返回包含id的数组，失败返回false
     */
    public function saveProject($flag, $title, $desc, $content) {
        try {
            error_log("开始保存项目: " . $flag);
            
            // 对输入数据进行验证和清理
            $flag = trim($flag);
            $title = trim($title);
            $desc = trim($desc);
            
            // 检查数据长度
            if (strlen($flag) > 255 || strlen($title) > 255 || strlen($desc) > 255) {
                throw new Exception("输入数据长度超出限制");
            }
            
            // 检查content是否为有效的JSON
            $contentJson = json_decode($content, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                throw new Exception("Content必须是有效的JSON格式");
            }
            
            // 检查content大小（例如最大1MB）
            if (strlen($content) > 1024 * 1024) {
                throw new Exception("Content大小不能超过1MB");
            }
            
            // 检查项目是否存在
            $existingProject = $this->getProjectByFlag($flag);
            error_log("检查项目是否存在: " . ($existingProject ? "存在" : "不存在"));
            
            if ($existingProject) {
                // 更新项目
                error_log("执行更新操作");
                $stmt = $this->pdo->prepare("UPDATE projects SET title = :title, `desc` = :desc, content = :content WHERE flag = :flag");
                $stmt->bindParam(':flag', $flag);
                $stmt->bindParam(':title', $title);
                $stmt->bindParam(':desc', $desc);
                $stmt->bindParam(':content', $content);
                
                $result = $stmt->execute();
                error_log("保存项目操作结果: " . ($result ? "成功" : "失败"));
                
                if ($result) {
                    // 返回项目ID
                    return ['id' => $existingProject['id'], 'flag' => $flag, 'title' => $title, 'desc' => $desc];
                }
                return false;
            } else {
                // 插入新项目
                error_log("执行插入操作");
                $stmt = $this->pdo->prepare("INSERT INTO projects (flag, title, `desc`, content) VALUES (:flag, :title, :desc, :content)");
                $stmt->bindParam(':flag', $flag);
                $stmt->bindParam(':title', $title);
                $stmt->bindParam(':desc', $desc);
                $stmt->bindParam(':content', $content);
                
                $result = $stmt->execute();
                error_log("保存项目操作结果: " . ($result ? "成功" : "失败"));
                
                if ($result) {
                    // 获取插入的ID
                    $id = $this->pdo->lastInsertId();
                    return ['id' => $id, 'flag' => $flag, 'title' => $title, 'desc' => $desc];
                }
                return false;
            }
        } catch (Exception $e) {
            error_log("保存项目失败: " . $e->getMessage());
            error_log("错误代码: " . $e->getCode());
            error_log("错误文件: " . $e->getFile());
            error_log("错误行号: " . $e->getLine());
            throw new Exception("保存项目失败: " . $e->getMessage());
        }
    }
    
    /**
     * 保存截图
     * @param int $id 项目ID
     * @param string $imageData 图片base64数据
     * @return bool 是否保存成功
     */
    public function saveScreenshotById($id, $imageData) {
        try {
            // 验证ID是否为有效整数
            if (!is_numeric($id) || $id <= 0) {
                throw new Exception("无效的项目ID");
            }
            
            $id = (int)$id;
            
            // 验证图片数据是否以正确的前缀开始
            if (strpos($imageData, 'data:image/png;base64,') !== 0) {
                throw new Exception("无效的图片数据格式");
            }
            
            // 移除数据URL前缀
            $base64Data = str_replace('data:image/png;base64,', '', $imageData);
            $base64Data = str_replace(' ', '+', $base64Data);
            
            // 检查base64数据是否有效
            if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $base64Data)) {
                throw new Exception("无效的base64数据");
            }
            
            // 解码base64数据
            $imageData = base64_decode($base64Data);
            
            // 验证解码后的数据是否为有效的PNG图片
            if (substr($imageData, 0, 8) !== "\x89PNG\x0d\x0a\x1a\x0a") {
                throw new Exception("图片数据不是有效的PNG格式");
            }
            
            // 确保截图目录存在
            $screenshotsDir = dirname(__DIR__) . '/screenshots';
            if (!file_exists($screenshotsDir)) {
                mkdir($screenshotsDir, 0777, true);
            }
            
            // 验证目录是否可写
            if (!is_writable($screenshotsDir)) {
                throw new Exception("截图目录不可写");
            }
            
            // 保存图片文件
            $imagePath = $screenshotsDir . '/' . $id . '.png';
            
            // 检查文件是否已存在，如果存在先删除
            if (file_exists($imagePath)) {
                unlink($imagePath);
            }
            
            $result = file_put_contents($imagePath, $imageData);
            
            // 验证文件是否保存成功
            if ($result === false) {
                throw new Exception("保存图片文件失败");
            }
            
            // 验证保存的文件是否为有效的PNG图片
            if (!file_exists($imagePath) || filesize($imagePath) == 0) {
                throw new Exception("保存的图片文件无效");
            }
            
            return true;
        } catch (Exception $e) {
            error_log("保存截图失败: " . $e->getMessage());
            throw new Exception("保存截图失败: " . $e->getMessage());
        }
    }
    
    /**
     * 保存截图 (保留兼容性)
     * @param string $flag 项目标识
     * @param string $imageData 图片base64数据
     * @return bool 是否保存成功
     */
    public function saveScreenshot($flag, $imageData) {
        try {
            // 验证flag是否有效
            if (empty($flag) || strlen($flag) > 255) {
                throw new Exception("无效的项目标识");
            }
            
            // 验证图片数据是否以正确的前缀开始
            if (strpos($imageData, 'data:image/png;base64,') !== 0) {
                throw new Exception("无效的图片数据格式");
            }
            
            // 移除数据URL前缀
            $base64Data = str_replace('data:image/png;base64,', '', $imageData);
            $base64Data = str_replace(' ', '+', $base64Data);
            
            // 检查base64数据是否有效
            if (!preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $base64Data)) {
                throw new Exception("无效的base64数据");
            }
            
            // 解码base64数据
            $imageData = base64_decode($base64Data);
            
            // 验证解码后的数据是否为有效的PNG图片
            if (substr($imageData, 0, 8) !== "\x89PNG\x0d\x0a\x1a\x0a") {
                throw new Exception("图片数据不是有效的PNG格式");
            }
            
            // 确保截图目录存在
            $screenshotsDir = dirname(__DIR__) . '/screenshots';
            if (!file_exists($screenshotsDir)) {
                mkdir($screenshotsDir, 0777, true);
            }
            
            // 验证目录是否可写
            if (!is_writable($screenshotsDir)) {
                throw new Exception("截图目录不可写");
            }
            
            // 保存图片文件
            $imagePath = $screenshotsDir . '/' . $flag . '.png';
            
            // 检查文件是否已存在，如果存在先删除
            if (file_exists($imagePath)) {
                unlink($imagePath);
            }
            
            $result = file_put_contents($imagePath, $imageData);
            
            // 验证文件是否保存成功
            if ($result === false) {
                throw new Exception("保存图片文件失败");
            }
            
            // 验证保存的文件是否为有效的PNG图片
            if (!file_exists($imagePath) || filesize($imagePath) == 0) {
                throw new Exception("保存的图片文件无效");
            }
            
            return true;
        } catch (Exception $e) {
            error_log("保存截图失败: " . $e->getMessage());
            throw new Exception("保存截图失败: " . $e->getMessage());
        }
    }
}